package com.eian.boot.module.system.role.service;

import com.eian.boot.common.core.enums.CommonEnum;
import com.eian.boot.common.core.exception.ExceptionHelper;
import com.eian.boot.common.core.utils.BeanCopierUtils;
import com.eian.boot.module.system.associations.role.dept.service.RoleDeptRelationService;
import com.eian.boot.module.system.associations.role.permission.service.RolePermissionRelationService;
import com.eian.boot.module.system.associations.user.role.service.UserRoleRelationService;
import com.eian.boot.module.system.permission.model.vo.PermissionVO;
import com.eian.boot.module.system.permission.model.vo.RolePermissionVO;
import com.eian.boot.module.system.permission.service.PermissionCacheService;
import com.eian.boot.module.system.role.mapper.SysRoleMapper;
import com.eian.boot.module.system.role.model.dto.*;
import com.eian.boot.module.system.role.model.entity.SysRoleEntity;
import com.eian.boot.module.system.role.model.vo.RoleDetailVO;
import com.eian.boot.module.system.role.model.vo.RoleListVO;
import com.eian.boot.mybatis.core.page.PageResult;
import com.eian.boot.mybatis.core.service.BaseService;
import com.mybatisflex.core.paginate.Page;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.eian.boot.common.constants.ErrorMessage.Role;
import static com.eian.boot.table.Tables.*;


/**
 * 角色表 服务层实现。
 *
 * @author alex.meng
 * @since 1.0.0
 */
@Service
@AllArgsConstructor
public class SysRoleService extends BaseService<SysRoleMapper, SysRoleEntity> {
    private final RolePermissionRelationService rolePermissionRelationService;
    private final UserRoleRelationService userRoleRelationService;
    private final PermissionCacheService cacheService;
    private final RoleDeptRelationService roleDeptRelationService;

    /**
     * @author alex.meng
     * @description 创建角色
     **/
    public void createRole(CreateRoleRequest params) {
        // 检查角色编码是否重复
        boolean codeExists = this.queryChain()
                .eq(SysRoleEntity::getCode, params.getCode())
                .exists();
        ExceptionHelper.ifTrue(codeExists, Role.CODE_EXIST);

        // 检查角色名称是否重复
        boolean nameExists = this.queryChain()
                .eq(SysRoleEntity::getName, params.getName())
                .exists();
        ExceptionHelper.ifTrue(nameExists, Role.NAME_EXIST);

        SysRoleEntity role = BeanCopierUtils.copy(params, SysRoleEntity.class);
        this.save(role);
    }

    /**
     * @author alex.meng
     * @description 获取角色详情
     **/
    public RoleDetailVO getRoleDetail(Long id) {
        SysRoleEntity role = this.getById(id);
        ExceptionHelper.ifNull(role, Role.NOT_EXIST);

        RoleDetailVO roleDetail = BeanCopierUtils.copy(role, RoleDetailVO.class);

        List<Long> permIds = rolePermissionRelationService.getPermissionIdsByRoleId(id);
        if (!permIds.isEmpty()) {
            List<PermissionVO> perms = this.queryChain()
                    .from(sys_permission)
                    .where(sys_permission.id.in(permIds))
                    .listAs(PermissionVO.class);
            roleDetail.setPerms(perms);
        } else {
            roleDetail.setPerms(List.of());
        }

        return roleDetail;
    }

    /**
     * @author alex.meng
     * @description 分页查询角色列表
     **/
    public PageResult<RoleListVO> getRoleList(RoleQueryRequest request) {
        Page<SysRoleEntity> page = this.queryChain()
                .like(SysRoleEntity::getName, request.getName())
                .like(SysRoleEntity::getCode, request.getCode())
                .eq(SysRoleEntity::getStatus, request.getStatus())
                .orderBy(SysRoleEntity::getId, false)
                .page(request.buildPage());

        List<SysRoleEntity> roles = page.getRecords();
        if (roles.isEmpty()) {
            return PageResult.build(page, RoleListVO.class);
        }

        List<Long> roleIds = roles.stream().map(SysRoleEntity::getId).toList();

        Map<Long, Long> userCountMap = userRoleRelationService.queryChain()
                .select(sys_user_role.role_id)
                .from(sys_user_role)
                .where(sys_user_role.role_id.in(roleIds))
                .groupBy(sys_user_role.role_id)
                .listAs(Long.class)
                .stream()
                .collect(Collectors.groupingBy(id -> id, Collectors.counting()));

        Map<Long, Long> permCountMap = rolePermissionRelationService.queryChain()
                .select(sys_role_permission.role_id)
                .from(sys_role_permission)
                .where(sys_role_permission.role_id.in(roleIds))
                .groupBy(sys_role_permission.role_id)
                .listAs(Long.class)
                .stream()
                .collect(Collectors.groupingBy(id -> id, Collectors.counting()));

        return PageResult.build(page, i -> {
            RoleListVO vo = BeanCopierUtils.copy(i, RoleListVO.class);
            vo.setUserCount(userCountMap.getOrDefault(i.getId(), 0L).intValue());
            vo.setPermCount(permCountMap.getOrDefault(i.getId(), 0L).intValue());
            return vo;
        });
    }

    /**
     * @author alex.meng
     * @description 更新角色信息
     **/
    public void updateRole(RoleUpdateRequest params) {
        SysRoleEntity role = this.getById(params.getId());
        ExceptionHelper.ifNull(role, Role.NOT_EXIST);

        // 检查角色名称是否重复
        if (StringUtils.isNotBlank(params.getName()) && !params.getName().equals(role.getName())) {
            boolean nameExists = this.queryChain()
                    .eq(SysRoleEntity::getName, params.getName())
                    .ne(SysRoleEntity::getId, role.getId())
                    .exists();
            ExceptionHelper.ifTrue(nameExists, Role.NAME_EXIST);
        }

        BeanCopierUtils.copy(params, role);
        this.updateById(role);
    }

    /**
     * @author alex.meng
     * @description 删除角色
     **/
    @Transactional(rollbackFor = Exception.class)
    public void delete(RoleDeleteRequest params) {
        SysRoleEntity role = this.getById(params.getId());
        ExceptionHelper.ifNull(role, Role.NOT_EXIST);

        if (ObjectUtils.isNotEmpty(params.getForce()) && params.getForce().equals(CommonEnum.YES.getValue())) {
            // 强制删除：解除所有角色绑定的用户（会触发事件清理缓存）
            userRoleRelationService.cascadeDeleteByRoleId(role.getId());
        } else {
            // 检查是否有用户绑定
            boolean hasBind = userRoleRelationService.checkRoleHasUsers(role.getId());
            ExceptionHelper.ifTrue(hasBind, Role.HAS_USER_BIND);
        }

        // 删除角色本身
        this.removeById(role);

        // 级联删除角色权限关系
        rolePermissionRelationService.cascadeDeleteByRoleId(role.getId());

        // 级联删除角色部门关系
        roleDeptRelationService.cascadeDeleteByRoleId(role.getId());

        // 清除角色权限缓存
        cacheService.clearRolePermissions(params.getId());
    }

    /**
     * @author alex.meng
     * @description 分配角色权限
     **/
    @Transactional(rollbackFor = Exception.class)
    public void assignRolePermission(RolePermissionAssignRequest params) {
        // 业务验证：角色存在性
        SysRoleEntity role = this.getById(params.getRoleId());
        ExceptionHelper.ifNull(role, Role.NOT_EXIST);

        // 业务验证：权限有效性
        long validPermCount = this.queryChain()
                .from(sys_permission)
                .where(sys_permission.id.in(params.getPermIds()))
                .count();
        ExceptionHelper.ifTrue(validPermCount != params.getPermIds().size(), Role.PERMISSION_INVALID);

        // 委托给关系服务处理（会触发事件清理缓存）
        boolean result = rolePermissionRelationService.assignRolePermissions(role.getId(), params.getPermIds());
        ExceptionHelper.ifFalse(result, Role.ASSIGN_PERMISSION_FAILED);
    }

    /**
     * @author alex.meng
     * @description 获取角色权限
     **/
    public RolePermissionVO getRolePermission(Long roleId) {
        SysRoleEntity role = this.getById(roleId);
        ExceptionHelper.ifNull(role, Role.NOT_EXIST);

        List<Long> permIds = rolePermissionRelationService.getPermissionIdsByRoleId(role.getId());
        RolePermissionVO result = BeanCopierUtils.copy(role, RolePermissionVO.class);
        result.setPermissions(permIds);
        return result;
    }

    /**
     * @author alex.meng
     * @description 获取用户角色列表
     **/
    public List<SysRoleEntity> getUserRoles(Long userId) {
        List<Long> roleIds = userRoleRelationService.getRoleIdsByUserId(userId);
        if (roleIds.isEmpty()) {
            return List.of();
        }
        return this.queryChain()
                .where(sys_role.id.in(roleIds))
                .list();
    }

    /**
     * @author alex.meng
     * @description 设置角色数据权限
     **/
    @Transactional(rollbackFor = Exception.class)
    public void setRoleDataScope(RoleDataScopeRequest request) {
        SysRoleEntity role = this.getById(request.getRoleId());
        ExceptionHelper.ifNull(role, Role.NOT_EXIST);

        // 更新角色数据范围
        role.setDataScope(request.getDataScope());
        this.updateById(role);

        // 如果是自定义数据权限，设置可访问部门
        if (request.getDataScope().equals(2)) { // 自定义
            roleDeptRelationService.setRoleDataScope(request.getRoleId(), request.getDeptIds());
        } else {
            // 清空自定义部门数据
            roleDeptRelationService.setRoleDataScope(request.getRoleId(), null);
        }

        // 清除相关用户缓存
        cacheService.clearAllUserCache();
    }

    /**
     * @author alex.meng
     * @description 获取角色数据权限
     **/
    public RoleDataScopeRequest getRoleDataScope(Long roleId) {
        SysRoleEntity role = this.getById(roleId);
        ExceptionHelper.ifNull(role, Role.NOT_EXIST);

        RoleDataScopeRequest result = new RoleDataScopeRequest();
        result.setRoleId(roleId);
        result.setDataScope(role.getDataScope());

        // 如果是自定义权限，获取部门列表
        if (role.getDataScope() != null && role.getDataScope().equals(2)) {
            List<Long> deptIds = roleDeptRelationService.getDeptIdsByRoleId(roleId);
            result.setDeptIds(deptIds);
        }

        return result;
    }
}